package com.vpa.main;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.AlertDialog.Builder;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnCancelListener;


import android.content.Intent;
import android.graphics.Typeface;
import android.os.Bundle;
import android.util.Log;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ListView;
import android.widget.TextView;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import com.vpa.R;
import com.vpa.graphics.GLView;
import com.vpa.graphics.GraphicsController;
import com.vpa.graphics.ThreadedGraphicsException;


public class MainActivity extends Activity  {
	private Lock lock_obj = new ReentrantLock();
	
	GraphicsController gfx;

	private static  ListView pid_listview = null;
	private static ArrayList<PID_Entry> list = null;
	private static PID_Store pid_store;

	private static LinkedList<String> send_queue = new LinkedList<String>();
	DataStore datastore = new DataStore();
	static final String mode = "01";
	public  Core core;
	private static boolean isRunning;
	private static final int REQUEST_ENABLE_BT = 2;
	private BluetoothAdapter bluetoothAdapter;
	public Activity context;
	private static final String logTag = "BluetoothTest";
	public static Dialog dialog;
	static int test_gauge;
	boolean textArea = false;
	
	
	/*Supported PIDS*/
	final int divisions = 7;
	final int elements = 20;
	
	long sup_20;
	long sup21_40;
	long sup41_60;
	long sup61_80;
	long sup81_100;
	long sup101_120;
	long sup121_140;
	
	

	

	
	/*List adapaters*/
	public static ProgressDialog prog;
	public static ArrayAdapter<Object> adapter;

	
	@Override
	public void onCreate(Bundle icicle) {
		super.onCreate(icicle);
		pid_store = new PID_Store();	
		bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
		if (bluetoothAdapter == null) {
			Log.d(logTag, "Could not get bluetooth adapter");
			return;
		}
		try {
			setContentView(R.layout.core_landing);
			/*Menu Button for Android Devices w/o physical menu button*/
			Button menu = (Button) findViewById(R.id.menubutton);
			menu.setOnClickListener(new OnClickListener() {
	            public void onClick(View v) {
	                openOptionsMenu();
	            }
	        });
			
			/*Build and populate the listview*/
			populateLV();
			
			gfx =  new GraphicsController((GLView)findViewById(com.vpa.R.id.glview), this);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		this.core = new Core(gfx,pid_store,this);
		/*try {
			//gfx.updateGauge((test_gauge = gfx.addAnalogGauge(-1500, 1500, R.drawable.gayge, R.drawable.pinkneedle, "RPM", "Rot/Min")), 100);
			//gfx.addAnalogGauge(100, 200, R.drawable.gayge, R.drawable.pinkneedle, "", "");
			//gfx.addDigitalGauge(R.drawable.digital, "", "");
		try {
			gfx.updateGauge((test_gauge = gfx.addAnalogGauge(-1500, 1500, R.drawable.gayge, R.drawable.pinkneedle, "RPM", "Rot/Min")), 1300);
//			gfx.addAnalogGauge(100, 200, R.drawable.gayge, R.drawable.pinkneedle, "", "");
//			gfx.addDigitalGauge(R.drawable.digital, "", "");
			int no = gfx.addTextArea(16);
			int ind = gfx.addTextAreaElement(no, "penis", "in/s");
			int ind2 = gfx.addTextAreaElement(no, "cock", "m/s");
			gfx.updateTextArea(no, ind, "55");
			gfx.updateTextArea(no, ind2, "31");

		} catch (ThreadedGraphicsException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		*/
		//searchForDevices();
		
	}
	

	private void populateLV(){
	    
	    int hmSize = pid_store.pid_map.size();
	    list = new ArrayList<PID_Entry>(hmSize + 1);
	    for(int i  = 0; i < hmSize; i++){
	    	
	    	PID_Entry t = pid_store.pid_map.get(i);
	    	if(t != null){
	    		list.add(t);
	    	}
	    }		
	    
	    Collections.sort(list);
	}
	
	
	
	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
	    MenuInflater inflater = getMenuInflater();
	    inflater.inflate(R.menu.core_menu, menu);
	    return true;
	}
	
	
	public boolean onOptionsItemSelected(MenuItem item) {
		switch (item.getItemId()) {
		    case R.id.add_gauge_item: 
		    	//lock_obj.lock();
			    Intent i = new Intent(getBaseContext(),AddGaugeListView.class);
			    startActivityForResult(i,1);
			//    lock_obj.unlock();
			    break;
		    case R.id.Exit:
		    	isRunning = false;
		    	break;
		    case R.id.Logging:
		    	item.setCheckable(true);
		    	if(item.isChecked()){
		    		item.setChecked(!item.isChecked());
		    		item.setTitle("Stop Logging");
		    	}
		    	else
		    	{
		    		item.setChecked(!item.isChecked());
		    		item.setTitle("Start Logging");
		    	}
		    	break;

		    default:
		    return super.onOptionsItemSelected(item);
		}
		return true;
	}

	@Override
	protected void onActivityResult(int requestCode, int resultCode, Intent data) {
		super.onActivityResult(requestCode, resultCode, data);
		if(data == null)
		{
			return;
		}
		if(data.hasExtra("canceled")){
			return;
		}
		if(requestCode == 1){
            int pid = data.getIntExtra("result",0);
			int gauge_type = data.getIntExtra("gauge",0);
			PID_Entry t = pid_store.pid_map.get(pid);
			runOnUiThread(new AddGauge(t.getMin(), t.getMax(), gauge_type,pid,t.getLabel(), t.getLabel())); /*passing label for desc and label*/
			return;
		}
		
		if (requestCode == REQUEST_ENABLE_BT) {
			if (requestCode == RESULT_OK) {
				Log.d("BluetoothTest", "bluetooth enabled");
				searchForDevices();
			} else {
				Log.d("BluetoothTest", "Could not enable bluetooth device");
			}
		}

	}

	public void lineReadFromBluetoothDevice(String line) {
		Log.d(logTag, "Line: " + line);
	}
	
	

	private void searchForDevices() {
		if (bluetoothAdapter.isEnabled()) {
			Set<BluetoothDevice> devicesAvailable = bluetoothAdapter.getBondedDevices();
			if (devicesAvailable.isEmpty()) {
				informUserNoDevicesPaired();
			} else {
				askUserToPickDeviceToUse(devicesAvailable);
			}
		} else {
			Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
			startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
		}
	}

	private void askUserToPickDeviceToUse(Set<BluetoothDevice> devicesAvailable) {
		final ListView   view = new ListView(this);
		view.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
		adapter = new ArrayAdapter<Object>(this, android.R.layout.simple_list_item_1, devicesAvailable.toArray(new BluetoothDevice[devicesAvailable.size()])) {

			@Override
			public View getView(int position, View convertView, ViewGroup parent) {
				TextView view = (TextView) super.getView(position, convertView, parent);
				BluetoothDevice bluetoothDevice = (BluetoothDevice) getItem(position);
				view.setText(bluetoothDevice.getName() + " : " + bluetoothDevice.getAddress());
				return view;
			}
		};
		view.setAdapter(adapter);
		view.setOnItemClickListener(new OnItemClickListener() {

			public void onItemClick(AdapterView<?> arg0, View arg1, int position, long arg3) {
				pairToDevice((BluetoothDevice) view.getItemAtPosition(position));
			}
		});

		dialog = new Dialog(this);
		dialog.setContentView(view);
		dialog.setCancelable(true);
		dialog.show();

	}

	private void informUserNoDevicesPaired() {
		Builder dialogBuilder = new AlertDialog.Builder(this);
		dialogBuilder.setMessage("No Device Paired");
		dialogBuilder.setPositiveButton("Ok", new DialogInterface.OnClickListener() {

			public void onClick(DialogInterface dialog, int arg1) {
				dialog.dismiss();

			}
		});
		dialogBuilder.setOnCancelListener(new OnCancelListener() {

			public void onCancel(DialogInterface dialog) {
				dialog.dismiss();
				finish();
			}
		});
		dialogBuilder.show();
	}

	private void showError(String message) {
	/*
		Builder dialogBuilder = new AlertDialog.Builder(this);
		dialogBuilder.setMessage("Error \n\n" + message);
		dialogBuilder.setOnCancelListener(new OnCancelListener() {

			public void onCancel(DialogInterface dialog) {
				MainActivity.prog.dismiss();
				finish();
			}
		});
		dialogBuilder.show();
		*/
	}

	private void pairToDevice(BluetoothDevice bluetoothDevice) {
		openSocket(bluetoothDevice);
	}

	private void openSocket(BluetoothDevice bluetoothDevice) {
		try {

			final ProgressDialog dialog = new ProgressDialog(this);
			final ConnectRunnable connector = new ConnectRunnable(bluetoothDevice, dialog);
			prog = ProgressDialog.show(this, "Connecting", "Connecting to " + bluetoothDevice.getName() + " : " + bluetoothDevice.getAddress(),
					true, true,
					new OnCancelListener() {
				public void onCancel(DialogInterface dialog) {
					connector.cancel();
				}
			});

			new Thread(connector).start();

		} catch (IOException ex) {
			Log.d(logTag, "Could not open bluetooth socket", ex);
			showError("Connection Failed: " + ex.getMessage());
		}
	}

	private void closeSocket(BluetoothSocket openSocket) {
		try {
			openSocket.close();
		} catch (IOException ex) {
			Log.d(logTag, "Could not close exisiting socket", ex);
		}
	}

	
	
	
	public class AddGauge implements Runnable{
	
		double min;
		double max;
		String label;
		String desc;
		int pid;
		int gauge_type;
		
		public AddGauge(double d, double e, int gauge_type,int pid, String label, String desc){
			this.gauge_type = gauge_type;
			this.min = d;
			this.max = e;
			this.label = label;
			this.desc = desc;
			this.pid = pid;
		}
		@Override
		public void run() {
			try {
				int gauge_id = -1;
				if(gauge_type == 0){
					gauge_id = gfx.addDigitalGauge(R.drawable.digital, label, desc);
				}else if(gauge_type == 1){
					gauge_id = gfx.addAnalogGauge((int)min, (int)max, R.drawable.gayge, R.drawable.pinkneedle, label, desc);
				}else if(gauge_type == 2){
					if(textArea == false){
						gauge_id = gfx.addTextArea(16);
						textArea = true;
						core.addTextArea(gauge_id);
					}
					core.addTextElement(pid,pid_store.pid_map.get(pid).getLabel(),pid_store.pid_map.get(pid).getLabel());
					String adding = String.format("%02X", pid);
	 				lock_obj.lock();
						send_queue.add(adding);
					lock_obj.unlock();
					return;
				}
				core.addGauge(pid,gauge_id);
				String adding = String.format("%02X", pid);
 				lock_obj.lock();
					send_queue.add(adding);
				lock_obj.unlock();
			} catch (ThreadedGraphicsException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}

		}
	}
	
	public class UpdateElement implements Runnable{
		int spot_num;
		private int id;
		private String value;
		int pid;
		
		public UpdateElement(int spot_num,int id,String value,int pid){
			this.spot_num = spot_num;
			this.id = id;
			this.value = value;
			this.pid = pid;
		}
		@Override
		public void run() {
			boolean isStillActive = gfx.updateTextArea(spot_num, id, value);
			if(!isStillActive){
				textArea = false;
				Iterator<Integer> it = core.text_elements.iterator();
				lock_obj.lock();
				while(it.hasNext())
					send_queue.remove(String.format("%02X", it.next()));
				lock_obj.unlock();
				core.clearTextArea();
			}
		}
	}
	
	
	
	public class UpdateGauge implements Runnable{
		private int id;
		private double value;
		
		public UpdateGauge(int id,double value){
			this.id = id;
			this.value = value;
		}
		@Override
		public void run() {
			boolean isStillActive = gfx.updateGauge(id,value);
			if(!isStillActive){
				int pid = core.removeGauge(id);
				lock_obj.lock();
					send_queue.remove(String.format("%02X", pid));
				lock_obj.unlock();
			}
		}
	}
	
	private class ConnectRunnable implements Runnable {

		private BluetoothSocket socket;

		public ConnectRunnable(BluetoothDevice device, ProgressDialog dialog) throws IOException {
			Method m;
			
			try {
				//if(Build.VERSION.SDK_INT <10 ){
					m = device.getClass().getMethod("createRfcommSocket", new Class[] {int.class});
					socket = (BluetoothSocket) m.invoke(device, 1);
				//}else{
	             //  socket = device.createInsecureRfcommSocketToServiceRecord(MY_UUID);
	              //  socket.connect();
				//}
			} catch (NoSuchMethodException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IllegalArgumentException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();	
			} catch (IllegalAccessException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (InvocationTargetException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}

		public void run() {
			bluetoothAdapter.cancelDiscovery();
			try {
					socket.connect();
					
				} catch (Exception e) {
				        Log.e(logTag, e.getMessage());
					}
				
				
			MainActivity.dialog.dismiss();
			runOnUiThread(new Runnable(){
			public void run(){
				prog.dismiss();
			}
			});
		    
			new Thread(new SocketCommunicator(socket)).start();

			
			
		}

		public void cancel() {
			try {
				socket.close();
			} catch (IOException e) {
				Log.d(logTag, "Canceled connection", e);
			}
		}
	}

	private class SocketCommunicator implements Runnable {

		private final BluetoothSocket socket;
		public SocketCommunicator(BluetoothSocket socket) {
			this.socket = socket;
		}

		@Override
		
		public void run() {
			/* ADDING OBSERVERS */
			datastore.addObserver(core);
			Log.d(logTag, "Listening on socket");

			try {
				final InputStream input = socket.getInputStream();
				final OutputStream output = socket.getOutputStream();
				
				final BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(input, "UTF-8"));
				String line;
				String next_line;
				Iterator<String> it;
				String pid;
				
				/* MESSAGE LOOP */
				isRunning = true;
				
				if (input.available() > 0 ){
					input.read();
				}

				/*Reset */
				output.write("ATWS\r".getBytes());
				while(true){
					String init = bufferedReader.readLine();
					if(init.contains("ELM327")){
						bufferedReader.readLine();
						break;
					}
				}

				
				/*Turn off echo & burn 2 line reads*/
				output.write("ATE0\r".getBytes());
				String flush = bufferedReader.readLine();
				while(!flush.equals("")){
					flush = bufferedReader.readLine();
				}
				flush = "F";
				try {
					Thread.sleep(500);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
					
				 /*Get the supported pids*/		
				/*
				 String temp;
				 temp = "";
			     output.write("0100\r".getBytes());
			     temp = readForResult(bufferedReader,"00");		     
			     temp = temp.substring(4,temp.length());
				 sup_20 = Long.parseLong(temp,16);
				 
				 
				 temp = "";
			   	 output.write("0120\r".getBytes());
			     temp = readForResult(bufferedReader,"20");
			   	 temp = temp.substring(4,temp.length());
				 sup21_40 = Long.parseLong(temp,16);
				 
				 temp = "";
				 output.write("0140\r".getBytes());
				 temp = readForResult(bufferedReader,"40");	     
				 temp = temp.substring(4,temp.length());
				 sup41_60 =Long.parseLong(temp,16);
				 
				 temp = "";
				 output.write("0160\r".getBytes());
				 temp = readForResult(bufferedReader,"60");
			     temp = temp.substring(4,temp.length());
				 sup61_80 = Long.parseLong(temp,16);
				 
				 temp = "";
				 output.write("0180\r".getBytes());
				 temp = readForResult(bufferedReader,"80");
			     temp = temp.substring(4,temp.length());
				 sup81_100  = Long.parseLong(temp,16);
				 
				 
				 temp = "";
				 output.write("01A0\r".getBytes());
				 temp = readForResult(bufferedReader,"1A");
			     temp = temp.substring(4,temp.length());
				 sup101_120  = Long.parseLong(temp,16);
				 
				 
				 temp = "";
				 output.write("01C0\r".getBytes());
				 temp = readForResult(bufferedReader,"C0");
			     temp = temp.substring(4,temp.length());
				 sup121_140  = Long.parseLong(temp,16);	
				*/
			
				while (isRunning) {
					
					
				output.write("0100\r".getBytes());
				flush = bufferedReader.readLine();
				while(!flush.equals("")){
					flush = bufferedReader.readLine();
				}


					lock_obj.lock();
					it = send_queue.iterator(); //Get a fresh iterator
					
			/*Ping to keep alive*/
					while(it.hasNext()){
						line = "";
						next_line = "";
 						pid = it.next();
 						
						output.write(("01" + pid + "\r").getBytes());	//Send bytes
						line = bufferedReader.readLine();
						while(line.equals("")){ 	//Check for line termination
							line = bufferedReader.readLine();
						}
						line = line.replace(" ", "").replace(">", "");	//Replace all spaces in lines
						if(line.contains("41")){
							datastore.add_entry(line);
						}
						
						flush = bufferedReader.readLine();
						while(!flush.equals("")){
							flush = bufferedReader.readLine();
						}
					}
					lock_obj.unlock();
				}
				
				socket.close();
				finish();
 			} catch (IOException ex) {
				Log.d("com.vpa",ex.getMessage());
			}
		}
		
		
		private String readForResult(BufferedReader fi, String mustContain) throws IOException{
			
			String temp = fi.readLine().replace(">", "").replace(" ", "");

			while(temp.equals("") || temp.contains("SEARCHING")){
				temp = readForResult(fi, mustContain);
			}
			if(temp.contains("N") || temp.contains("P") || temp.contains("R")){
				temp = "000000000000";
			}
			else if(temp.length() < 4){
				temp = readForResult(fi, mustContain);
			}
			else if(!temp.substring(0,temp.length()).contains(mustContain)){
				temp = readForResult(fi, mustContain);
			}
			
			
			String flush = " ";
			while(!flush.equals("")){
				flush = fi.readLine();
			}
			return temp;
		}
	

		@SuppressWarnings("unused")
		public void cancel() {
			try {
				socket.close();
			} catch (IOException e) {
			}
		}
	}
	
	/*ListView*/
	  public static class ViewHolder {
	        public TextView textView;
	    }
	
	public static class AddGaugeListView extends Activity {

		  Intent returnIntent;
		  @Override
		  protected void onCreate(Bundle savedInstanceState) {
		    super.onCreate(savedInstanceState);
		    setContentView(R.layout.add_gauge_listview);
	    	  returnIntent = new Intent();
	    	  
		    pid_listview = (ListView) findViewById(R.id.add_gauge_listview);
		    final StableArrayAdapter adapter = new StableArrayAdapter(this,android.R.layout.simple_list_item_1, list);
		    pid_listview.setAdapter(adapter);
		    
		    registerForContextMenu(pid_listview);
		    
		    pid_listview.setOnItemClickListener(new AdapterView.OnItemClickListener() {

		    	
		    
		    	
		      @Override
		      public void onItemClick(AdapterView<?> parent, final View view,int position, long id) {
		    	  returnIntent.putExtra("result",list.get(position).getPid());
		    	  setResult(RESULT_OK,returnIntent);     
		    	  openContextMenu(view);
		      }
		    });
		  }
		  
			 @Override  
			    public void onCreateContextMenu(ContextMenu menu, View v,ContextMenuInfo menuInfo) {  
			 super.onCreateContextMenu(menu, v, menuInfo);  
			     menu.setHeaderTitle("Select Gauge Type");  
			     menu.add(0, v.getId(), 0, "Digital");  
			     menu.add(0, v.getId(), 0, "Analog");  
			     menu.add(0, v.getId(), 0, "Text Area"); 
			 }  
			 
			       @Override  
			    public boolean onContextItemSelected(MenuItem item) {  
			    	   
			            if(item.getTitle()=="Digital"){
			            	returnIntent.putExtra("gauge",0);
			            	finish();
			            }  
			        else if(item.getTitle()=="Analog"){
			        	returnIntent.putExtra("gauge",1);
		            	finish();
			        }  
			        else if(item.getTitle()=="Text Area"){
			        	returnIntent.putExtra("gauge",2);
		            	finish();
			        } 
			        else {return false;}  
			    return true;  
			    }  

		  private class StableArrayAdapter extends ArrayAdapter<PID_Entry> {

		    HashMap<String, Integer> mIdMap = new HashMap<String, Integer>();

		    public StableArrayAdapter(Context context, int textViewResourceId,
		        ArrayList<PID_Entry> list) {
		      super(context, textViewResourceId,list);
		      for (int i = 0; i < list.size(); ++i) {
		        mIdMap.put(list.get(i).toString(), i);
		      }
		    }		 
		    
		    @Override
	        public View getView(int position, View convertView, ViewGroup parent) {
	            ViewHolder holder = null;
	            if (convertView == null) {
	                convertView = getLayoutInflater().inflate(android.R.layout.simple_list_item_1, null);
	                holder = new ViewHolder();
	                holder.textView = (TextView)convertView.findViewById(android.R.id.text1);
	                convertView.setTag(holder);
	               // if(!list.get(position).getDynamic()){
		            //	holder.textView.setTypeface(null,Typeface.BOLD);
		            //}
	            } else {
	                holder = (ViewHolder)convertView.getTag();
	            }
	            holder.textView.setText(list.get(position).toString());
	           
	            return convertView;
	        }
		    		    
		    @Override
		    public boolean hasStableIds() {
		      return true;
		    }
		    
		  }
		} 
}